<?php
/*
 * kernel/object.file.php
 *
 * This code is distributed and used under the GNU GPL v2 License,
 * please, read file LICENSE, which is situated in root directory,
 * or in "LICENSE" directory with most acceptable translation for you
 *
 * Files and directories common things
 *
 * Copyright (C) 2010, Taras Kudla
 *
 */

define("_FILENAME_SIZE",20);

final class gFile extends gKernel {
	
    private $filestable = "files";

    /**
     * Files object initialization
     *
     */
    public function init(){
        
    }
    /**
     * Creates Gepard usable name from specified filename
     *
     * @param <type> $name
     * @return <type>
     */
    private function encodefilename($name){
        $val = rand(30, 100);
        $name = preg_replace("/[^A-z0-9_]/","",$name);
        $name = $val.substr($name,0,15).md5($name.microtime());
        $name = substr($name,0,_FILENAME_SIZE);
        return $name;
    }
    /**
     * Copies specified image file and resizes it due to requirement width/height
     *
     * @param integer $id       File id in system table
     * @param integer $width    New file width
     * @param integer $height   New file height
     * @return integer          New file id
     */
    public function resizeimage($id, $width, $height = null){
        # id - image file id, if height == null ,uses aspect ratio to height
        $_    = $this->filenamebyid($id);
        $__   = $this->parseextension($_);
        $name = crc32(md5($_) . microtime());
        $__   = strtolower($__);
        if ($__ == "jpg" || $__ == "jpeg"){
            $im = imagecreatefromjpeg($this->core->parseasdir($this->core->get("uploadsdir")).$_);
        }
        if ($__ == "png"){
            $im = imagecreatefromstring(file_get_contents($this->core->parseasdir($this->core->get("uploadsdir")). $_));
            imagealphablending($im, true);
            imagesavealpha($im, true);
        }
        if ($__ == "gif"){
            $im = imagecreatefromgif($this->core->parseasdir($this->core->get("uploadsdir")). $_);
            imagealphablending($im, true);
            imagesavealpha($im, true);
        }
        if (!$im){
            $this->core->error("file","Error in image resizing. Cannot create image from file.");
            return false;
        }
        $x = imagesx($im);
        $y = imagesy($im);
        $w = null;
        $h = null;
        if ($x < $width && $width != null)
            return true;
        if ($y < $height && $height != null && $width == null)
            return true;
        if ($height == null){
            $w = $width;
            $h = round(($y / $x) * $w);
        }else{
            $w = $width;
            $h = $height;
        }
        if ($width == null && $height != null){
            $w = round(($x / $y) * $h);
            $h = $height;
        }
        $tmp = imagecreatetruecolor($w, $h);
        imagecopyresampled($tmp, $im, 0, 0, 0, 0, round($w), round($h), $x, $y);
        if ($__ == "jpg" || $__ == "jpeg")
            $t = @imagejpeg($tmp, $this->core->parseasdir($this->core->get("uploadsdir")). $_, 100);
        if ($__ == "png")
            $t = @imagepng($tmp, $this->core->parseasdir($this->core->get("uploadsdir")). $_);
        if ($__ == "gif")
            $t = @imagegif($tmp, $this->core->parseasdir($this->core->get("uploadsdir")). $_);
        imagedestroy($im);
        imagedestroy($tmp);
        if (!$t){
            $this->core->error("file","Error in image resizing");
            return false;
        }
        # saving data in db
        $size = filesize($this->core->parseasdir($this->core->get("uploadsdir")). $_);
        $this->core->dbo->update($this->filestable, "set size='" . (int) $size . "' where id='" . (int) $id . "' ");
        return $id;
    }
    /**
     * Returns file url in system uploads path
     *
     * @param mixed $name   File id or name
     * @return string
     */
    public function getfileurl($name){
        # checking if id
        if( is_numeric($name)){
            $name = $this->filenamebyid($name);
        }
        if( !$name)
            return false;
        return $this->core->parseasurl($this->core->get("uploadsurl")).$name;
    }
    /**
     * Copies installed file from system files table to another
     *
     * @param integer $id       File id from system files table
     * @return integer
     */
    public function copysysfile($id){
        $n       = $this->filenamebyid($id);
        $newname = $this->encodefilename($n). "." . $this->parseextension($n);
        $type    = $this->getmimetype($n);
        $size    = filesize($this->core->parseasdir($this->core->get("uploadsdir")). $n);
        $c       = @copy($this->core->parseasdir($this->core->get("uploadsdir")). $n, $this->core->parseasdir($this->core->get("uploadsdir")). $newname);
        if ($c){
            $file = $this->insertfiledata($newname, $type, $size);
        if (is_numeric($file))
            return $file;
        }
        $this->core->error("file","cannot copy file");
            return false;
    }
	
    /**
     * Checks whether file is image
     *
     * @param string $filename  Full file name
     * @return boolean
     */
    public function is_image($filename){
        $mime = $this->getmimetype($filename);
        $array = explode("/",$mime);
        return ($array[0]=="image")?true:false;
    }

    /**
     * Returns file mimetype
     *
     * @param string $filename  Full file name
     * @return string
     */
    public function getmimetype($filename){
        /*
         * some of code of this method was copied from php.net
         * http://us2.php.net/manual/en/function.mime-content-type.php#87856
         */
        if( function_exists("mime_content_type")){
            return mime_content_type($filename);
        }
        $mime_types = array(
            'txt' => 'text/plain',
            'htm' => 'text/html',
            'html' => 'text/html',
            'php' => 'text/html',
            'css' => 'text/css',
            'js' => 'application/javascript',
            'json' => 'application/json',
            'xml' => 'application/xml',
            'swf' => 'application/x-shockwave-flash',
            'flv' => 'video/x-flv',
            # images
            'png' => 'image/png',
            'jpe' => 'image/jpeg',
            'jpeg' => 'image/jpeg',
            'jpg' => 'image/jpeg',
            'gif' => 'image/gif',
            'bmp' => 'image/bmp',
            'ico' => 'image/vnd.microsoft.icon',
            'tiff' => 'image/tiff',
            'tif' => 'image/tiff',
            'svg' => 'image/svg+xml',
            'svgz' => 'image/svg+xml',
            # archives
            'zip' => 'application/zip',
            'rar' => 'application/x-rar-compressed',
            'exe' => 'application/x-msdownload',
            'msi' => 'application/x-msdownload',
            'cab' => 'application/vnd.ms-cab-compressed',
            # audio/video
            'mp3' => 'audio/mpeg',
            'qt' => 'video/quicktime',
            'mov' => 'video/quicktime',
            # adobe
            'pdf' => 'application/pdf',
            'psd' => 'image/vnd.adobe.photoshop',
            'ai' => 'application/postscript',
            'eps' => 'application/postscript',
            'ps' => 'application/postscript',
            # ms office
            'doc' => 'application/msword',
            'docx' => 'application/msword',
            'rtf' => 'application/rtf',
            'xls' => 'application/vnd.ms-excel',
            'ppt' => 'application/vnd.ms-powerpoint',
            # open office
            'odt' => 'application/vnd.oasis.opendocument.text',
            'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
        );
        $ext = strtolower(array_pop(explode('.',$filename)));
        if (array_key_exists($ext, $mime_types)) {
            return $mime_types[$ext];
        } elseif (function_exists('finfo_open')) {
            $finfo = finfo_open(FILEINFO_MIME);
            $mimetype = finfo_file($finfo, $filename);
            finfo_close($finfo);
            return $mimetype;
        } else {
            return 'unknown/'.(strlen($ext)>4?"unknown":$ext);
        }
    }
	
    /**
     * Moves uploaded file to Gepard uploads path and binds it with system files table
     *
     * @param string $area      Html file input field name
     * @param array  $exts      Array of allowed file extentions
     * @return integer
     */
    public function copyuploadedfile($area, $exts = null){
        if (!isset($_FILES[$area])){
            return false;
        }
        $name  = $_FILES[$area]["name"];
        $type  = $_FILES[$area]["type"];
        $size  = $_FILES[$area]["size"];
        $tname = $_FILES[$area]["tmp_name"];
        $ext   = $this->parseextension($name);
        if (is_array($exts) && !in_array(strtolower($ext), $exts)){
            $this->core->error("file","file type is not supported");
            return false;
        }
        $ext = strtolower($ext);
        if ($this->is_image($name) && !getimagesize($tname)){
            return false;
        }
        $rname = $this->encodefilename($name . rand(100, 999)) . ($ext != null ? (($ext == 'php' || $ext == 'phtml' || $ext == 'htm' || $ext == 'html' || $ext == 'xml') ? ".txt" : "." . $ext) : null);
        $_ = copy($tname, $this->core->parseasdir($this->core->get("uploadsdir")). $rname);
        if ($_){
            $file = $this->insertfiledata($rname, $type, $size);
        }else{
            return false;
        }
        # resize, if file is image
        if ($this->is_image($name)){
            list($width, $height, $type, $attr) = getimagesize($this->core->parseasdir($this->core->get("uploadsdir")). $rname);
            $this->resizeimage($file, $width - 1, $height - 1);
        }
        return $file;
    }
        
    /**
     * Gets image size (width/height)
     *
     * @param mixed $id     File id in system table or file full name
     * @return mixed
     */
    public function imagesize($id){
        $filename = $this->filenamebyid($id);
        $filename = $this->core->parseasdir($this->core->get("uploadsdir")).$filename;
        if( !file_exists($filename))
            return false;
        if( !$this->is_image($filename))
            return false;
        return getimagesize($filename);
    }

    /**
     * Binds existed file to system files table
     *
     * @param string $filename      File full path
     * @return integer  File id in table
     */
    public function bindfile($filename){
        if( file_exists($filename)){
            $this->core->error("file","Cannot bind file to system database, file '$filename' not found");
            return false;
        }
        $type = $this->getmimetype($filename);
        preg_match("/^.+([^\/\\\]+)$/U",$filename,$tmp);
        $name = $tmp[1];
        $size = filesize($filename);
        $id = $this->insertfiledata($name,$type,$size);
        return $id;
    }
    /**
     * Inserts file information into Gepard system table
     *
     * @param string  $name     File name
     * @param string  $type     Type of file (mime)
     * @param integer $size     Filesize
     * @param string  $source   File siurce (from what it was added)
     * @return integer  New file id
     */
    public function insertfiledata($name, $type, $size,$source=null){
        $name=$this->core->escapeString($name);
        $fileid=$this->core->dbo->insert($this->filestable,array(
            "name" => $name,
            "type" => $type,
            "size" => (int) $size,
            "source"=>( $source!=null)?$source:($this->core->getenvlayer()==_ENV_KERNEL?"kernel":$this->core->getenvcaller("mod")),
            "cdate" => date("Y-m-d H:i:s")
            ),"id");
        return $fileid;
    }
    /**
     * Returns file extension
     *
     * @param <type> $name
     * @return <type>
     */
    public function parseextension($name){
        $data = explode(".", $name);
        return (count($data) == 0 || !is_array($data)) ? null : $data[count($data) - 1];
    }

    /**
     * Gets Gepard file name
     *
     * @param <type> $id
     * @return <type>
     */
    public function filenamebyid($id){
        $name = $this->core->dbo->select($this->filestable, "name", "where id='".(int)$id."' limit 1 offset 0",_RESULT_SINGLE);
        return (!$name)?false:$name;
    }

    /**
     * Deletes a file
     * 
     * @param mixed $id     Gepard file id, or filename, or filename with full path
     * @return boolean
     */
    public function delete($id){
        if( $id==0 )
            return true;
        $name= $this->core->dbo->select($this->filestable, "name", "where id='" . $this->core->escapeString($id) . "' limit 1 offset 0",_RESULT_SINGLE);
        if($name==null)
            return true;
        $filename = $this->core->parseasdir($this->core->get("uploadsdir")).$name;
        if( file_exists($filename)){
            unlink($filename);
        }
        $this->core->dbo->delete($this->filestable, " where id='" . $id . "' ");
        return true;
    }

    /**
     * Gets all files in a directory
     *
     * @param string $dir       Directory full path
     * @param string $match     Pattern to match filename
     * @return array
     */
    public function getdirfiles($dir,$match=null){
        $files = array();
        if( $d = opendir($dir)){
            while (false !== ($file = readdir($d))) {
                if ($file != "." && $file != "..") {
                    if( $match!=null){
                        if( !preg_match($match,$file)){
                        continue;
                        }
                    }
                    $files[]=$file;
                }
            }
        }
        closedir($d);
        return $files;
    }

    /** Copies directory recursivelly
     *
     * @param string $src   Source directory path
     * @param string $dst   Destination directory path
     */
    public function copydir($src,$dst) {
        $dir = opendir($src);
        if( !is_dir($dst))
            mkdir($dst);
        while(false !== ( $file = readdir($dir)) ) {
            if ( is_dir($src.'/'.$file) ) {
                $this->copydir($src . '/' . $file,$dst . '/' . $file);
            } else {
                copy($src.'/'.$file,$dst.'/'.$file);
            }
        }
        closedir($dir);
    }

    /**
     * Removes directory recursivelly
     *
     * @param string $dir   Full path to directory
     * @return boolean
     */
    public function deletedir($dir) {
        if (!file_exists($dir)) return true;
        if (!is_dir($dir)) return unlink($dir);
        foreach (scandir($dir) as $item) {
            if ($item == '.' || $item == '..') continue;
            if (!$this->deletedir($dir.DIRECTORY_SEPARATOR.$item)) return false;
        }
        return rmdir($dir);
    }
}
